/*
 * Copyright 2007-2008 Michele Mostarda ( michele.mostarda@gmail.com ).
 * All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package org.asemantics.anthill;

import org.jivesoftware.smack.packet.Message;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 *  Implements the Invoke Command message. This command invokes a target with
 *  a given environment on the remote listener.
 *
 * @author Michele Mostarda
 */
public class InvokeCommandMessage extends CommandMessage {

    /**
     * The name of the message parameter storing the remote target.
     * This is a meta-parameter.
     */
    public static final String COMMAND_TARGET_PARAMETER = "command_target";

    /**
     * The name of every command target is prefixed with this stirng to avoid conflict with
     * meta parameters.
     */
    public static final String COMMAND_TARGET_PARAMETER_PREFIX = "target_";

    /**
     * The prefix is added to every parameter name to avoid conflicts with meta-parameters.
     */
    public static final String PARAMETER_PREFIX = "parameter_";

    /**
     * Constructor.
     *
     * @param commandTarget the command name is the name of the remote target to invoke.
     * @param parameters a map of parameter name / value strings.
     */
    public InvokeCommandMessage(String commandTarget, Map<String,Object> parameters) {
        super(Invoke.class);
        init(commandTarget, parameters, true);
    }

    /**
     * Constructor.
     * Access point used during introspection creation.
     *
     * @param map
     */
    public InvokeCommandMessage(Map<String,Object> map) {
        super(Invoke.class);
        
        if(map == null) {
            throw  new IllegalArgumentException("map cannot be null");
        }
        String command = (String) map.get(COMMAND_TARGET_PARAMETER);
        if(command == null) {
            throw new IllegalArgumentException("Invalid parameters: cannot find command");
        }
        command = command.substring(COMMAND_TARGET_PARAMETER_PREFIX.length());
        init(command, map, false);
    }

    /**
     * Common initialization method.
     * @param commandTarget
     * @param parameters
     * @param boxing TODO: find better solution for boxing.
     */
    private void init(String commandTarget, Map<String,Object> parameters, boolean boxing) {
        if(commandTarget == null || commandTarget.trim().length() == 0) {
            throw new IllegalArgumentException("Invalid commandTarget: " + commandTarget);
        }

        if(parameters == null) {
            throw new NullPointerException("Illegal parameter value");
        }

        // Sets the target name.
        setProperty(
                COMMAND_TARGET_PARAMETER,
                COMMAND_TARGET_PARAMETER_PREFIX + commandTarget
        );

        // Sets the target parameters.
        for( Map.Entry<String,Object> entry : parameters.entrySet() ) {
            setProperty( (boxing ? PARAMETER_PREFIX : "") + entry.getKey(), entry.getValue());
        }
    }



    /**
     * Extracts the parameters from an {@link InvokeCommandMessage}.
     *
     * @param message
     * @return
     * @throws IllegalArgumentException if the given message is not a <i>CommandMessage</i>.
     */
    public static Map<String,String> getParameters(Message message) {
        return extractParameters(message);
    }

    /**
     * Extracts the command name from an {@link InvokeCommandMessage}.
     *
     * @param message
     * @return
     * @throws IllegalArgumentException if the given message is not a <i>CommandMessage</i>.
     */
    public static String getCommand(Message message) {
        String parameterValue = (String) message.getProperty(COMMAND_TARGET_PARAMETER);
        return parameterValue.substring(COMMAND_TARGET_PARAMETER_PREFIX.length());
    }

    /**
     * Extract the parameters map from a message.
     *
     * @param message
     * @return
     */
    protected static Map<String, String> extractParameters(Message message) {
        Map result = new HashMap<String, String>();
        Iterator<String> propertyNames = message.getPropertyNames().iterator();
        String propertyName;
        while (propertyNames.hasNext()) {
            propertyName = propertyNames.next();
            if(propertyName.indexOf(PARAMETER_PREFIX) == 0) {
                result.put( propertyName.substring(PARAMETER_PREFIX.length()), message.getProperty(propertyName) );
            }
        }
        return result;
    }

}
